#encoding: utf-8

from flask import Flask, render_template, request, redirect, url_for, session, g, flash, jsonify, Response
import config
from exts import db
from models import User, Question, Answer
from decorator import login_required
from sqlalchemy import or_
from werkzeug.routing import BaseConverter
from datetime import datetime

app = Flask(__name__)
# 引入配置文件
app.config.from_object(config)
# db绑定app
db.init_app(app)

# 自定义URL转换器：一个url中，含有手机号码的变量，必须限定这个变量的字符串格式满足手机号格式
class TelephoneConveter(BaseConverter):
    regex = r'1[345789]\d{9}'

# 用户在访问/posts/a+b
class ListConveter(BaseConverter):
    # 把参数a+b等内容，根据“+”进行切割，形成列表
    def to_python(self, value):
        return value.split("+")

    # 把url_for的参数反转结果自动拼接为a+b的形式
    def to_url(self, value):
        return "+".join(value)

# 添加转换器
app.url_map.converters['tel'] = TelephoneConveter
app.url_map.converters['arr'] = ListConveter

# 将视图函数中返回的字典，转换成json对象，然后返回
# Restful API都是通过JSON的形式进行传递，如果你的后台跟前台进行交互，所有的URL都是发送JSON数据，那么此时你可以自定义一个叫做JSONResponse的类来代替Flask自带的Response类：
# 自定义Response
class JSONResponse(Response):
    @classmethod
    def force_type(cls, response, environ=None):
        """
        这个方法只有视图函数返回非字符串、非元祖、非Response对象才会调用。
        :param response:
        :param environ:
        :return:
        """
        # 将字典转化为json格式
        if isinstance(response, dict):
            response = jsonify(response)
        return super(JSONResponse, cls).force_type(response, environ)

app.response_class = JSONResponse

@app.route('/')
@app.route('/index/')
def index():
    context = {
        'questions': Question.query.order_by('-create_time').all()
    }
    return render_template('index.html', **context)

@app.route('/detail/<question_id>/')
def detail(question_id):
    context = {
        'question': Question.query.filter(Question.id == question_id).first()
    }
    return render_template('detail.html',**context)

@app.route('/add_answer/',methods=['POST'])
@login_required
def add_answer():
    content = request.form.get('answer_content')
    question_id = request.form.get('question_id')
    answer = Answer(content=content)
    # 生成回答者对应关系
    answer.author = g.user
    # 生成问题对应关系
    question = Question.query.filter(Question.id == question_id).first()
    answer.question = question
    # 保存数据
    db.session.add(answer)
    db.session.commit()

    return redirect(url_for('detail',question_id=question_id))

@app.route('/regist/',methods=['GET','POST'])
def regist():
    if request.method == 'GET':
        return render_template('regist.html')
    else:
        telephone = request.form.get('telephone')
        username = request.form.get('username')
        password1 = request.form.get('password1')
        password2 = request.form.get('password2')

        # 数据有效性校验
        if telephone == '':
            flash(u'手机号不能为空！')
            return render_template('regist.html')
        if len(telephone) != 11:
            flash(u'手机号格式不正确！')
            return render_template('regist.html')
            # return u'手机号格式不正确'
        if username == '':
            flash(u'用户名不能为空！')
            return render_template('regist.html')
            # return u'用户名不能为空'
        if password1 == '' or password2 == '':
            flash(u'密码不能为空！')
            return render_template('regist.html')
            # return u'密码不能为空'

        # 手机号码验证。不允许重复注册
        user = User.query.filter(User.telephone == telephone).first()
        if user:
            flash(u'该手机号已被注册，请更换手机号码！')
            return render_template('regist.html')
            # return u'该手机号已被注册，请更换手机号码！'
        else:
           #     password1和password2相等才行
            if password1 != password2:
                flash(u'两次密码不相等，请核对后再填写')
                return render_template('regist.html')
                # return u'两次密码不相等，请核对后再填写'
            else:
                user = User(telephone=telephone,username=username,password=password1)
                db.session.add(user)
                db.session.commit()
                # 如果注册成功，就跳转到登录页面
                return redirect(url_for('login'))

@app.route('/login/',methods=['GET','POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        telephone = request.form.get('telephone')
        password = request.form.get('password')

        # 数据有效性校验
        if telephone == '':
            flash(u'手机号不能为空！')
            return render_template('login.html')
            # return u'手机号不能为空'
        elif len(telephone) != 11:
            flash(u'手机号格式不正确！')
            return render_template('login.html')
            # return u'手机号格式不正确'
        if password == '':
            flash(u'密码不能为空！')
            return render_template('login.html')
            # return u'密码不能为空'

        # 登录处理
        user = User.query.filter(User.telephone == telephone).first()
        if user and user.check_password(password):
            session['user_id'] = user.id
            # 如果想在多天内都不需要登录
            session.permanent = True

            return redirect(url_for('index'))
        else:
            flash(u'手机号或密码不正确！')
            return render_template('login.html')

@app.route('/logout/')
def logout():
    session.clear()
    return redirect(url_for('login'))

@app.route('/question/',methods=['GET','POST'])
@login_required
def question():
    if request.method == 'GET':
        return render_template('question.html')
    else:
        title = request.form.get('title')
        content = request.form.get('content')
        # 校验数据有效性
        if title == '':
            return u'标题不能为空'
        if content == '':
            return u'内容不能为空'
        # 保存问答数据
        question = Question(title=title, content=content)
        question.author = g.user
        db.session.add(question)
        db.session.commit()
        # 如果发布成功，就跳转到首页
        return redirect(url_for('index'))

@app.route('/search/')
def search():
    q = request.args.get('q')
    # 或(or)查询
    where = or_(Question.title.contains(q),Question.content.contains(q))
    questions = Question.query.filter(where).order_by('-create_time')
    # 与(and)查询
    # questions = Question.query.filter(Question.title.contains(q),Question.content.contains(q)).order_by('-create_time')
    return render_template('index.html', questions=questions, q=q)

##########################
##### 与问答系统无关 #####

# 演示自定义URL转换器arr的使用
@app.route('/posts/<arr:boards>')
def posts(boards):
    # to_url()
    print(url_for('posts', boards=['a', 'b']))
    # to_python()
    return "%s" % boards

@app.route('/head/')
def head():
    heads = {
        'token': 'j02jjt02h0ff904bhg',
        'name': 'Tim',
        'sex': '男'
    }
    # 返回元祖。元祖中的数据类型是(response,status,headers)
    return 'heads', 200, heads

@app.route('/json/')
def getJson():
    # 返回字典时，自定义的JSONReponse会自动转换为json返回
    return {'name':'Tim', 'age':'23'}

############## 自定义模板过滤器 start ###################
@app.route('/filter/')
def filter():
    content = {
        'date_time': datetime(2018,3,5,23,47,0)
    }
    return render_template('test/filter.html', **content)

# jinjia2自定义过滤器（时间）
@app.template_filter('handle_time')
def handle_time(time):
    """
    time距离现在的时间间隔：
    1、如果时间间隔小于1分钟以内，那么就显示“刚刚”
    2、如果是大于1分钟小于1小时，那么就显示“XX分钟前”
    3、如果是大于1小时小于24小时，那么就显示“XX小时前”
    4、如果是大于24小时小于30天以内，那么就显示“XX天前”
    5、否则就是显示具体的时间
    :param time:
    :return:
    """
    if isinstance(time, datetime):
        now = datetime.now()
        timestamp = (now - time).total_seconds()
        if timestamp < 60:
            return u"刚刚"
        elif timestamp >= 60 and timestamp < 3600:
            minutes = timestamp / 60
            return u"%s分钟前" % int(minutes)
        elif timestamp >= 3600 and timestamp < 86400:
            hours = timestamp / 3600
            return u"%s小时前" % int(hours)
        elif timestamp >= 86400 and timestamp < 2592000:
            day = timestamp / 86400
            return u"%s天前" % int(day)
        else:
            return time.strftime('%Y/%m/%d %H:%M')
    else:
        return time
############## 自定义模板过滤器 end ###################

@app.route('/test/')
def test_index():
    return render_template('test/index.html')

@app.route('/statement/')
def statement():
    content = {
        "null_list": [],
        "users": ['user1', 'user2', 'user3'],
        "person": {
            "name": 'Tim',
            "age": '18',
            "country": 'china'
        },
        "books": [
            {
                "name": u"三国演义",
                "author": u"罗贯中",
                "price": "164.15"
            },{
                "name": u"西游记",
                "author": u"吴承恩",
                "price": "54.15"
            },{
                "name": u"红楼梦",
                "author": u"曹雪芹",
                "price": "64.15"
            },{
                "name": u"水浒传",
                "author": u"施耐庵",
                "price": "464.15"
            }
        ]
    }
    return render_template('test/statement.html', **content)

##########################
# 豆瓣微信小程序效果demo
@app.route('/douban/index/')
def douban():
    movies = [
        {
            'thumbnail': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2514175916.webp',
            'title': u'红海行动',
            'rating': '9.9'
        },{
            'thumbnail': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2514175916.webp',
            'title': u'植物大战僵尸',
            'rating': '2.1'
        },{
            'thumbnail': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2514175916.webp',
            'title': u'西游记',
            'rating': '8.4'
        },{
            'thumbnail': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2514175916.webp',
            'title': u'蓝海星动',
            'rating': '5.9'
        }
    ]
    context = {
        "movies": movies
    }
    return render_template('test/douban/index.html', **context)


##########################
@app.before_request
def my_before_request():
    user_id = session.get('user_id')
    if user_id:
        user = User.query.filter(User.id == user_id).first()
        if user:
            g.user = user

@app.context_processor
def my_context_processor():
    """
    上下文处理器（context_processor），必须要返回一个字典。假如返回内容为空，那也要返回一个空字典！
    :return: user信息
    """
    if hasattr(g, 'user'):
        return {'user': g.user}
    return {}

# g函数、视图函数、上下文处理器执行顺序
# before_request -> 视图函数 -> context_processor

if __name__ == '__main__':
    # 模板内容修改后，自动重启服务器
    app.config['TEMPLATES_AUTO_RELOADE'] = True
    # 运行本项目，host=0.0.0.0可以让其他电脑也能访问到该网站，port指定访问的端口。默认的host是127.0.0.1，port为5000
    # app.run(host='0.0.0.0',port=9000)
    app.run()